<?php

namespace app\checkin\controller;

use app\checkin\model\Member as MemberModel;
use app\checkin\model\Store as StoreModel;
use app\checkin\model\MemberStore as MemberStoreModel;
use think\admin\helper\QueryHelper;
use think\facade\Db;

/**
 * 会员管理
 * @class Member
 * @package app\checkin\controller
 */
class Member extends Base
{
    /**
     * 会员列表
     * @auth true
     * @menu true
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function index()
    {
        $this->title = '会员列表';
        MemberModel::mQuery()->layTable(function () {
            $this->assign('statusMap', MemberModel::$statusMap);
            $this->assign('stores', $this->getAccessibleStores());
        }, function (QueryHelper $query) {
            // 分别处理三个搜索字段，提高性能
            $name = trim($this->request->get('name', ''));
            $phone = trim($this->request->get('phone', ''));
            $cardNo = trim($this->request->get('card_no', ''));

            // 姓名搜索
            if ($name !== '') {
                $query->whereLike('name', "%{$name}%");
            }

            // 手机号搜索
            if ($phone !== '') {
                $query->whereLike('phone', "%{$phone}%");
            }

            // 卡号搜索（后4位）
            if ($cardNo !== '') {
                $query->whereRaw('RIGHT(card_no,4) = :card_no', ['card_no' => $cardNo]);
            }

            // 状态和门店筛选
            $query->equal('status,register_store_id')->dateBetween('create_time');

            if (!$this->isMaster) {
                $relTable = MemberStoreModel::mk()->getTable();
                $sid = $this->storeId;
                $query->where(function ($q) use ($relTable, $sid) {
                    $q->where(['register_store_id' => $sid])
                      ->whereOrRaw("id IN (SELECT member_id FROM {$relTable} WHERE store_id = :sid)", ['sid' => $sid]);
                });
            }

            // 处理排序参数（支持前端 Layui 的排序功能）
            // Layui 发送的参数是 _field_ 和 _order_
            $sortField = trim($this->request->get('_field_', trim($this->request->get('field', 'create_time'))));
            $sortType = trim($this->request->get('_order_', trim($this->request->get('order', 'desc'))));

            // 只允许排序这些字段，防止 SQL 注入
            $allowedFields = ['id', 'name', 'phone', 'card_no', 'total_points', 'available_points', 'create_time', 'update_time'];
            if (!in_array($sortField, $allowedFields)) {
                $sortField = 'create_time';
            }

            // 只允许 asc 或 desc
            if (!in_array(strtolower($sortType), ['asc', 'desc'])) {
                $sortType = 'desc';
            }

            $query->order($sortField, $sortType);

            $query->with(['registerStore' => static function ($q) {
                $q->field('id,name');
            }]);
        });
    }

    /**
     * 添加会员
     * @auth true
     */
    public function add()
    {
        $this->title = '添加会员';
        $this->_applyFormToken();
        MemberModel::mForm('form');
    }

    /**
     * 编辑会员
     * @auth true
     */
    public function edit()
    {
        $this->title = '编辑会员';
        $this->_applyFormToken();
        MemberModel::mForm('form');
    }

    /**
     * 表单数据过滤
     * @param array $data
     */
    protected function _form_filter(array &$data)
    {
        if ($this->request->isGet()) {
            $this->assign('isMaster', $this->isMaster);
            $this->assign('stores', $this->getAccessibleStores());
            $this->assign('statusMap', MemberModel::$statusMap);
            if (!$this->isMaster) {
                $data['register_store_id'] = $data['register_store_id'] ?? $this->storeId;
            }
        } else {
            if (!$this->isMaster) $data['register_store_id'] = $this->storeId;
            $data['name'] = trim($data['name'] ?? '');
            $data['card_no'] = trim($data['card_no'] ?? '');
            $data['phone'] = trim($data['phone'] ?? '');
            $data['id_card'] = trim($data['id_card'] ?? '');
            $data['img'] = trim($data['img'] ?? '');
            $data['status'] = intval($data['status'] ?? 1);

            // 验证必填项
            if ($data['name'] === '') $this->error('会员姓名不能为空！');
            if ($data['card_no'] === '') $this->error('磁卡号不能为空！');

            // 验证磁卡号唯一性
            $map = [['card_no', '=', $data['card_no']]];
            if (!empty($data['id'])) $map[] = ['id', '<>', $data['id']];
            if (MemberModel::mk()->where($map)->count() > 0) $this->error('磁卡号已存在，请更换！');

            // 验证手机号唯一性（如果填写了手机号）
            if ($data['phone'] !== '') {
                $phoneMap = [['phone', '=', $data['phone']]];
                if (!empty($data['id'])) $phoneMap[] = ['id', '<>', $data['id']];
                if (MemberModel::mk()->where($phoneMap)->count() > 0) $this->error('手机号已存在，请更换！');
            }

            // 处理密码：留空不变；有值则存储为 bcrypt 哈希（若已是哈希 $2y$/$argon2 则直接存）
            $pwd = trim($data['password'] ?? '');
            if ($pwd === '') {
                unset($data['password']);
            } else {
                if (stripos($pwd, '$2y$') === 0 || stripos($pwd, '$argon2') === 0) {
                    // 已经是哈希
                    $data['password'] = $pwd;
                } else {
                    $data['password'] = password_hash($pwd, PASSWORD_BCRYPT);
                }
            }

            if (empty($data['id'])) {
                $data['total_points'] = intval($data['total_points'] ?? 0);
                $data['used_points'] = intval($data['used_points'] ?? 0);
                $data['available_points'] = intval($data['available_points'] ?? 0);
            } else {
                // 编辑时：只允许修改可用积分，其他积分字段不能修改
                unset($data['total_points'], $data['used_points']);
                $data['available_points'] = intval($data['available_points'] ?? 0);
            }
        }
    }

    /**
     * 表单提交结果
     * @param boolean $result
     */
    protected function _form_result(bool $result)
    {
        if ($result) {
            // 入队人员同步任务：在设备侧拉取详情之前，先推送变动通知（operater=1/2/3）
            try {
                // 判断是新增还是编辑
                $isEdit = !empty($this->request->post('id', 0));
                $operater = $isEdit ? 2 : 1; // 1=新增, 2=修改, 3=删除

                // 确定会员ID（表单保存成功后应存在 $this->request->post('id') 或 $this->_vali 结果）
                $mid = intval($this->request->post('id', 0));
                if ($mid <= 0 && isset($this->request->post()['id'])) {
                    $mid = intval($this->request->post('id'));
                }

                // 若未取到ID，尝试从模型最近一次插入获取（兜底，不一定总是可行）
                if ($mid <= 0 && isset($this->data['id'])) {
                    $mid = intval($this->data['id']);
                }

                if ($mid > 0) {
                    // 编辑时，如果修改了可用积分，需要更新Redis缓存
                    if ($isEdit) {
                        $this->updateMemberCacheIfNeeded($mid);
                    }

                    // 确保同步队列表存在
                    $this->ensureSyncQueueTable();

                    // 获取所有启用设备 SN
                    $devices = \app\checkin\model\Device::mk()->where(['status' => 1])->column('sn');
                    $now = date('Y-m-d H:i:s');
                    foreach ($devices as $sn) {
                        Db::name('device_sync_queue')->insert([
                            'device_sn'  => strval($sn),
                            'mid'        => $mid,
                            'operater'   => $operater,
                            'message_id' => '',
                            'status'     => 0, // pending
                            'tries'      => 0,
                            'last_error' => '',
                            'create_time'=> $now,
                            'update_time'=> $now,
                        ]);
                    }

                    $actionText = $operater == 1 ? '新增' : '修改';
                    if (function_exists('trace')) {
                        trace("Member sync queued: mid={$mid}, operater={$operater}({$actionText}), devices=" . count($devices), 'info');
                    }
                }
            } catch (\Throwable $e) {
                // 入队失败不阻断用户保存，仅记录日志
                if (function_exists('trace')) trace('enqueue sync error: ' . $e->getMessage(), 'error');
                try {
                    // 表不存在则尝试创建后不再重试插入
                    $this->ensureSyncQueueTable();
                } catch (\Throwable $e2) { /* ignore */ }
            }

            $this->success('会员保存成功！');
        }
    }

    /**
     * 编辑会员时，如果修改了可用积分，更新Redis缓存
     * @param int $memberId 会员ID
     */
    private function updateMemberCacheIfNeeded(int $memberId)
    {
        try {
            // 从数据库查询最新的会员数据
            $member = MemberModel::mk()->where(['id' => $memberId])->findOrEmpty();
            if ($member->isEmpty()) {
                return;
            }

            // 获取Redis连接
            $redis = new \Redis();
            $redisConfig = config('cache.stores.redis');
            if (!$redisConfig) {
                // 如果没有配置，尝试从.env读取
                $redisConfig = [
                    'host' => env('REDIS_HOST', '127.0.0.1'),
                    'port' => env('REDIS_PORT', 6379),
                    'password' => env('REDIS_PASSWORD', ''),
                    'database' => env('REDIS_DATABASE', 0),
                ];
            }

            $redis->connect($redisConfig['host'] ?? '127.0.0.1', $redisConfig['port'] ?? 6379, 2);
            if (!empty($redisConfig['password'])) {
                $redis->auth($redisConfig['password']);
            }
            $redis->select($redisConfig['database'] ?? 0);

            $cacheKey = "member:{$memberId}";

            // 检查缓存是否存在
            if ($redis->exists($cacheKey)) {
                // 获取缓存数据
                $cacheData = json_decode($redis->get($cacheKey), true);
                if ($cacheData && is_array($cacheData)) {
                    // 更新缓存中的积分信息为最新的数据库值
                    $cacheData['available_points'] = intval($member->getAttr('available_points'));
                    $cacheData['total_points'] = intval($member->getAttr('total_points'));

                    // 重新保存到Redis（保持原有TTL）
                    $ttl = $redis->ttl($cacheKey);
                    if ($ttl > 0) {
                        $redis->setex($cacheKey, $ttl, json_encode($cacheData, JSON_UNESCAPED_UNICODE));
                    }
                }
            }

            $redis->close();
        } catch (\Throwable $e) {
            // Redis更新失败不影响业务流程，只记录日志
            \think\facade\Log::warning("Failed to update member cache after edit: " . $e->getMessage());
        }
    }

    /**
     * 确保设备同步队列表存在
     */
    private function ensureSyncQueueTable(): void
    {
        Db::execute(<<<SQL
CREATE TABLE IF NOT EXISTS `device_sync_queue` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `device_sn` VARCHAR(64) NOT NULL,
  `mid` INT UNSIGNED NOT NULL,
  `operater` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1新增 2修改 3删除',
  `message_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '推送时使用的消息ID',
  `status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0待发送 1已发送 2已确认 9失败',
  `tries` INT UNSIGNED NOT NULL DEFAULT 0,
  `last_error` VARCHAR(255) DEFAULT NULL,
  `create_time` DATETIME DEFAULT NULL,
  `update_time` DATETIME DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_sn_status` (`device_sn`,`status`),
  KEY `idx_msg` (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备人员同步队列表';
SQL);
    }

    /**
     * 修改状态
     * @auth true
     */
    public function state()
    {
        MemberModel::mSave($this->_vali([
            'status.in:0,1'  => '状态值范围异常！',
            'status.require' => '状态值不能为空！',
        ]));
    }

    /**
     * 删除
     * @auth true
     */
    public function remove()
    {
        MemberModel::mDelete();
    }

    /**
     * 批量更新会员信息（更新update_time）
     * @auth true
     */
    public function updateCreateTime()
    {
        // 获取会员总数
        $total = MemberModel::mk()->count();

        if ($total == 0) {
            $this->error('没有会员需要更新！');
        }

        // 直接更新所有会员的 update_time（添加条件 id > 0 以满足ThinkPHP要求）
        MemberModel::mk()->where('id', '>', 0)->update([
            'update_time' => date('Y-m-d H:i:s')
        ]);

        $this->success("成功更新 {$total} 个会员的信息！");
    }
}
